package com.ideacome.test.common.mock.config;

import com.ideacome.test.common.mock.config.annotation.AnnotationMockOperationSource;
import com.ideacome.test.common.mock.interceptor.MockInterceptor;
import com.ideacome.test.common.mock.interceptor.MockKeyGenerator;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cache.config.CacheManagementConfigUtils;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.w3c.dom.Element;

/**
 * 定义读取配置文件
 *
 * @author 罗成
 */
class AnnotationDrivenMockBeanDefinitionParser implements BeanDefinitionParser {


    /**
     * Parses the '{@code <cache:annotation-driven>}' tag. Will
     * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator} with
     * the container as necessary.
     */
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        String mode = element.getAttribute("mode");
        if ("aspectj".equals(mode)) {
            // mode="aspectj"
            throw new IllegalArgumentException("目前不支持AspectJ代理，想要支持自己写O(∩_∩)O");
        } else {
            // mode="proxy"
            registerCacheAdvisor(element, parserContext);
        }
        return null;
    }


    private void registerCacheAdvisor(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
        SpringMockingConfigurer.registerCacheAdvisor(element, parserContext);
    }


    /**
     * Configure the necessary infrastructure to support the Spring's caching annotations.
     */
    private static class SpringMockingConfigurer {

        private static void registerCacheAdvisor(Element element, ParserContext parserContext) {
            if (!parserContext.getRegistry()
                    .containsBeanDefinition(CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)) {
                Object eleSource = parserContext.extractSource(element);

                // 创建AnnotationMockOperationSource
                RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationMockOperationSource.class);
                sourceDef.setSource(eleSource);
                sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                parserContext.getRegistry().registerBeanDefinition(MockConfigUtils.MOCK_OPERATION_SOURCE_BEAN_NAME,
                        sourceDef);

                //创建 mockKeyGenerator
                RootBeanDefinition mockKeyGeneratorDef = new RootBeanDefinition(MockKeyGenerator.class);
                mockKeyGeneratorDef.setSource(eleSource);
                mockKeyGeneratorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                if (element.hasAttribute("keyPrefix")) {
                    mockKeyGeneratorDef.getPropertyValues().add("keyPrefix", element.getAttribute("keyPrefix"));
                }
                String mockKeyGeneratorName = parserContext.getReaderContext().registerWithGeneratedName(mockKeyGeneratorDef);

                // 创建 MockInterceptor
                RootBeanDefinition interceptorDef = new RootBeanDefinition(MockInterceptor.class);
                interceptorDef.setSource(eleSource);
                interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                interceptorDef.getPropertyValues().add("cacheOperationSources",
                        new RuntimeBeanReference(MockConfigUtils.MOCK_OPERATION_SOURCE_BEAN_NAME));
                if (element.hasAttribute("needMock")) {
                    interceptorDef.getPropertyValues().add("needMock", element.getAttribute("needMock"));
                }
                interceptorDef.getPropertyValues().add("keyGenerator", new RuntimeBeanReference(mockKeyGeneratorName));
                parserContext.getRegistry().registerBeanDefinition(MockConfigUtils.MOCK_INTERCEPTOR_BEAN_NAME,
                        interceptorDef);

                // 创建mockOperationSourceAdvisor
                RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheOperationSourceAdvisor.class);
                advisorDef.setSource(eleSource);
                advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                advisorDef.getPropertyValues().add("cacheOperationSource",
                        new RuntimeBeanReference(MockConfigUtils.MOCK_OPERATION_SOURCE_BEAN_NAME));
                advisorDef.getPropertyValues().add("adviceBeanName", MockConfigUtils.MOCK_INTERCEPTOR_BEAN_NAME);
                if (element.hasAttribute("order")) {
                    advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
                }
                parserContext.getRegistry().registerBeanDefinition(
                        MockConfigUtils.MOCK_OPERATION_SOURCE_ADVISOR_BEAN_NAME, advisorDef);
                CompositeComponentDefinition compositeDef =
                        new CompositeComponentDefinition(element.getTagName(), eleSource);
                compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef,
                        MockConfigUtils.MOCK_OPERATION_SOURCE_BEAN_NAME));
                compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef,
                        MockConfigUtils.MOCK_INTERCEPTOR_BEAN_NAME));
                compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef,
                        CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME));
                parserContext.registerComponent(compositeDef);
            }
        }

    }

}
